package DPOP;

import java.util.ArrayList;
import java.util.Scanner;

import jade.core.AID;
import jade.core.Agent;
import jade.core.behaviours.OneShotBehaviour;
import jade.core.behaviours.TickerBehaviour;
import jade.lang.acl.ACLMessage;
import jade.lang.acl.UnreadableException;

public class N1_old extends Agent {
	String myName = new String();
	String rootName = "pg1";
	XMLReader reader;
	//String DFS = "";
	//String p = new String();
	String[][] ngh; 		     // Neighbors {name, commCost, status, ownerAgent}

	int[][] join = new int[4][3];
	int[][] util = new int[4][3];
	final int UTIL = 1;
	final int VALUE = 2;

	TreeNode[] tn;

	boolean DEBUG = true;
	boolean rootPresent = false;
	private static final long serialVersionUID = 1L;
	ACLMessage rMsg;
	Scanner scan;

	// Put agent initializations here
	protected void setup()
	{
		scan = new Scanner(System.in);
		reader = new XMLReader("..//"+this.getLocalName()+".xml", false);
		myName = this.getLocalName();

		// Printout a welcome message
		System.out.println("Hello! I am agent "+this.getLocalName());

		// how many total number of neighbor variables
		int nbRelatedVariables = 0;
		for (int i=0; i<reader.getNbNeighbors(); i++) {
			nbRelatedVariables = nbRelatedVariables + reader.getNeighbors()[i].getNbRelatedVariables();
		}
		tn = new TreeNode[nbRelatedVariables+reader.getNbVariables()];
		//ngh = new String[][4];
		int ii = 0;
		for (int i=0; i<reader.getNbNeighbors(); i++) {
			tn[i].neighbors.add(new ArrayList<String>());
			for ( int j=0; j<reader.getNeighbors()[i].getNbRelatedVariables(); j++) {
				tn[i].neighbors.get(j).add(reader.getNeighbors()[i].getRelatedVariables()[j][0]);
				tn[i].neighbors.get(j).add(reader.getNeighbors()[i].getCommCost());
				tn[i].neighbors.get(j).add("unvisited");
				tn[i].neighbors.get(j).add(reader.getNeighbors()[i].getAgentName());
			}
			ii = i;
		}

		for (int i=(ii+1), j=0; i<nbRelatedVariables+reader.getNbVariables(); i++, j++) {
			tn[i].neighbors.get(j).add((reader.getVariables())[j][0]);
			tn[i].neighbors.get(j).add("0");
			tn[i].neighbors.get(j).add("unvisited");
			tn[i].neighbors.get(j).add(reader.getAgentName());
		}

		// Printing out ngh array
		if (DEBUG) {
			myPrint("length = "+tn.length);
			for (int i=0; i<tn.length;i++) {
				for (int j=0; j<tn[i].neighbors.size(); j++) {
					for (int k=0; k<tn[i].neighbors.get(j).size(); k++) {
						myPrint("tn["+i+"].get("+j+").get( = "+tn[i].neighbors.get(j).get(k)+"\t");
					}
				}
				myPrint("");	
			}
		}

		for (int i=0; i<tn.length; i++) {
			addBehaviour(new OneShotBehaviour()
			{
				private static final long serialVersionUID = 1L;

				public void action() {
					myPrint("Going into tokenpassing");
					tokenPassing();
					doDelete();
				}// End onTick


				public void tokenPassing() {
					//ACLMessage rMsg;
					for (int i=0; i<ngh.length; i++) {
						if (rootName.equals(ngh[i][0])) {
							rootPresent = true;
							tn[0].parent = "";
							tn[0].DFS = "";
							break;
						}
					}

					if(rootPresent == false) {
						myPrint("Going inside handleIncomingTokens");
						tn[0].DFS = handleIncomingTokens();
						myPrint("Returned DFS is "+tn[0].DFS);
					}

					// Sort neighbors list according to communication cost
					for (int a = 0; a < ( ngh.length - 1 ); a++) {
						for (int b = 0; b < ngh.length - a - 1; b++) {
							if (Integer.parseInt(ngh[b][1]) > Integer.parseInt(ngh[b+1][1])) {
								String[] swap = {ngh[b][0], ngh[b][1], ngh[b][2], ngh[b][3]};
								ngh[b][0]     = ngh[b+1][0];
								ngh[b][1]     = ngh[b+1][1];
								ngh[b][2]     = ngh[b+1][2];
								ngh[b][3]     = ngh[b+1][3];
								ngh[b+1][0]   = swap[0];
								ngh[b+1][1]   = swap[1];
								ngh[b+1][2]   = swap[2];
								ngh[b+1][3]   = swap[3];
							} // end if
						} // end inner for
					} // end outer for
					myPrint("After Sorting--");
					for (int i = 0; i<ngh.length; i++) {
						myPrint("\t"+ngh[i][0]+"\t"+ngh[i][1]+"\t"+ngh[i][2]+"\t"+ngh[i][3]);
					}

					// Why is it here?
					DFS = DFS + ngh[0][0];
					myPrint("DFS after adding my name = "+DFS);
					//scan.next();
					// Iterate through all unvisited neighbors
					for (int i = 0; i<(ngh.length); i++){
						if (ngh[i][2].equals("unvisited")) {
							if (ngh[i][3].equals(myName)) 
								myPrint("Sending message to myself");
							else
								myPrint(myName+" is Sending messages to my unvisited neighbor "+ngh[i][3]);
							// adding neighbor to child list
							if (!pp.contains(ngh[i][0]))
								c.add(ngh[i][0]);
							// Send DFS message to this child node
							ACLMessage sMsg = new ACLMessage(ACLMessage.INFORM);	
							sMsg.addReceiver(new AID(((ngh[i][3].equals(""))?reader.getAgentName():ngh[i][3]), AID.ISLOCALNAME));
							sMsg.setContent(DFS);
							myPrint("Sending DFS ("+DFS+") to "+((ngh[i][3].equals(""))?reader.getAgentName():ngh[i][3]));
							send(sMsg);
							// wait for the child to return the message
							myPrint("waiting to receive back DFS message");
							//rMsg = blockingReceive();
							// if this rMsg is not from the child then it is from pseudo child
							do {
								rMsg = blockingReceive();
								if (!(rMsg.getSender().getLocalName().equals((ngh[i][3].equals(""))?reader.getAgentName():ngh[i][3]))) {
									myPrint("Some other message received");
									String tempDFS = handleIncomingTokens();
									ACLMessage sMsg1 = new ACLMessage(ACLMessage.INFORM);
									myPrint("Sending DFS = "+tempDFS+" back to "+rMsg.getSender().getLocalName());
									sMsg1.addReceiver(rMsg.getSender());
									sMsg1.setContent(tempDFS);
									send(sMsg1);
								}
								else {
									myPrint("DFS returned from the child "+((ngh[i][3].equals(""))?reader.getAgentName():ngh[i][3]));
								}
							}while (!(rMsg.getSender().getLocalName().equals((ngh[i][3].equals(""))?reader.getAgentName():ngh[i][3])));
							// receive the DFS message from the child and update DFS
							if (rMsg != null){
								if(rMsg.getSender().getLocalName().equals((ngh[i][3].equals(""))?reader.getAgentName():ngh[i][3])){
									DFS = rMsg.getContent();
								}
							}
						} // end if
					} // end for
					/**
					 * DONE:
					 *   remove myName from DFS 
					 *   send DFS to its parent 
					 */
					// subtree of this agent has been completely explored
					// remove itself from the DFS and send it to its parent
					DFS  = DFS.replace(myName, "");
					ACLMessage sMsg = new ACLMessage(ACLMessage.INFORM);
					sMsg.addReceiver(new AID(p, AID.ISLOCALNAME));
					sMsg.setContent(DFS);
					send(sMsg);
				} // end procedure tokenPassing


			});// end oneShot behavior
		}
	}// End setup

	String handleIncomingTokens() {
		/**
		 *  TODO:
		 *     receive ACLMessage
		 */
		String DFSl = new String();
		// if no messages have been received yet
		if (DFSCount == 0 && !myName.equals(rootName)) {
			myPrint("blocking receive");
			rMsg = blockingReceive();
		}
		// message received
		if (rMsg != null) {
			DFSCount++;
			DFSl = rMsg.getContent();
			myPrint("Received DFS is "+DFSl);
			// mark msg.sender() visited in the ngh array
			for (int i=0; i<ngh.length; i++) {
				if (ngh[i][0].equals(rMsg.getSender().getLocalName())) {
					myPrint("Marking sender visited "+ngh[i][0]);
					ngh[i][2] = "visited";
				}
			}

			// if first message
			if (DFSCount == 1 && !(myName.equals(rootName))) {
				p = rMsg.getSender().getLocalName();
				for (int i = 0; i<ngh.length; i++){
					// if DFSl contains a neighbors 
					//which is not my parent then it is my pseudo parent
					myPrint("DFSl = "+DFSl+"\tParent = "+p+"\tngh[i][0] = "+ngh[i][0]);
					myPrint("C1 = "+DFSl.contains(ngh[i][0]));
					myPrint("C2 = "+!(ngh[i][0].equals(p)));
					myPrint("C3 = "+(DFSl.contains(ngh[i][0]) && !(ngh[i][0].equals(p))));
					if (DFSl.contains(ngh[i][0]) && !(ngh[i][0].equals(p))) {
						myPrint("Adding Pseudo Parent "+ngh[i][0]);
						pp.add(ngh[i][0]);
						//ngh[i][2] = "visited";
					}
				}
			}
			else { // not my first message
				// Sort unvisited neighbors
				if (c.contains(rMsg.getSender().getLocalName()));
				else {
					myPrint("Adding Pseudo Child "+ rMsg.getSender().getLocalName());
					pc.add(rMsg.getSender().getLocalName());
					for (int i=0; i<ngh.length; i++) {
						if (ngh[i][0].equals(rMsg.getSender().getLocalName())) {
							ngh[i][2] = "visited";
						}
					}
				}
			}
		}
		myPrint("Returning DFS = "+DFSl);
		return DFSl;
	}

	public void runUTILPropogation() {
		// join all util messages from the children as they arrive
		for (int i=0; i<c.size(); i++) {
			ACLMessage m1 = blockingReceive();
			if ((c.contains(m1.getSender().getLocalName()) && (m1.getPerformative() == UTIL))) {
				int temp[][] = null;
				try {
					temp = (int[][]) m1.getContentObject();
				} catch (UnreadableException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				for (int j=0; j<3; j++) {
					for (int k=0; k<4; k++) 
						join[j][k] = join[j][k] + temp[j][k];	
				} // end for
			}// end if
		} // end for


		for (int i=0; i<3; i++) {
			for (int j=0; j<4; j++) ;
			//join[i][j] = join[i][j] + relation_A3A1[i][j];
		} // end for

		// join all relations with pseduoparent
		for (int k=0; k<pp.size(); k++) {
			for (int i=0; i<3; i++) {
				for (int j=0; j<4; j++) ;
				//join[i][j] = join[i][j] + relation_A3A1[i][j];
			} // end inner for
		} // end outer for

		// use projection to eliminate self

		// send this util message to parent
	}

	public void runVALUEPropogation() {
		ACLMessage m1 = null;
		if (!myName.equals(rootName)){
			m1 = blockingReceive();
		}
		// pick your best value
		int myVal = Integer.parseInt(m1.getContent());

		// send this value to all your children
		ACLMessage m2 = new ACLMessage(VALUE);
		m2.setContent(Integer.toString(myVal));
		m2.setPerformative(VALUE);
		for (String child:c)
			m2.setSender(new AID(child, AID.ISLOCALNAME));
	}
	public void myPrint(String s) {
		if (DEBUG) 
			System.err.println(this.getLocalName()+": "+s+"");
	}
	public Agent getAgent(){ return this;}

	// To terminate the client agent
	public void takeDown() {
		String s = "\n-----------------------------\n";
		s = s + this.getLocalName() + " is terminating!";
		s = s + "\n\tParent: "+p;
		s = s + "\n\tChildren: ";
		for (String temp:c) {
			s =  s + temp + ", ";
		}
		s = s + "\n\tPseudo Parents: ";
		for (String temp:pp) {
			s = s + temp + ", ";
		}
		s = s + "\n\tPseudo Children: ";
		for (String temp:pc) {
			s = s + temp + ", ";
		}
		System.out.println(s +"\n-----------------------------\n");
	}
}
